import tkinter as tk
import json
import math
import os
from PIL import Image, ImageTk, ImageEnhance
from dataclasses import dataclass
# 读取 JSON 文件并转换为字典
def read_json_to_map(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        data = json.load(file)  # 解析 JSON 文件内容为字典
    return data


class DependeceLine:
    def __init__(self, canvas, x1=0, y1=0, x2=100, y2=100, radius = 32, line_color="white", line_width=2, id = None):
        """
        初始化 DependeceLine 类，在指定的 canvas 上绘制直线
        :param canvas: 传入的 Canvas 对象
        :param x1: 直线起点的 x 坐标
        :param y1: 直线起点的 y 坐标
        :param x2: 直线终点的 x 坐标
        :param y2: 直线终点的 y 坐标
        :param line_color: 直线的颜色
        :param line_width: 直线的宽度
        """
        self.canvas = canvas  # 传入的画布对象

        # 提取坐标
        self.x1 = x1
        self.x2 = x2
        self.y1 = y1
        self.y2 = y2
        self.radius = radius

        self.line_color = line_color
        self.line_width = line_width

        # 调用绘制方法
        self.draw_line()

    def draw_line(self):
        # 计算方向向量
        dx = self.x2 - self.x1
        dy = self.y2 - self.y1

        # 计算两圆心之间的距离
        dist = math.sqrt(dx**2 + dy**2)

        # 防止圆心重合时出错
        if dist == 0:
            return
        # 计算第一个圆的边缘点
        self.edge_x1 = self.x1 + (self.radius / dist) * dx
        self.edge_y1 = self.y1 + (self.radius / dist) * dy

        # 计算第二个圆的边缘点
        self.edge_x2 = self.x2 - (self.radius / dist) * dx
        self.edge_y2 = self.y2 - (self.radius / dist) * dy

        """在传入的画布上绘制一条直线"""
        self.line = self.canvas.create_line(
            self.edge_x1, self.edge_y1, self.edge_x2, self.edge_y2,
            fill=self.line_color, width=self.line_width, tags="line"
        )
    def on_resize(self, scale_x, scale_y, scale):
        print(self, "on_resize")
        self.x1 = self.x1 * scale_x
        self.x2 = self.x2 * scale_x
        self.y1 = self.y1 * scale_y
        self.y2 = self.y2 * scale_y
        self.radius = self.radius * scale
        if self.line is not None:
            self.canvas.delete(self.line)
        self.draw_line()


class TalentCircle(tk.Frame):
    def __init__(self, parent, canvas, center_x, center_y, radius, text_content, image_path, id):
        """
        创建一个带有悬停文本的圆圈。
        
        参数:
        - parent: 父容器
        - center_x: 圆心 X 坐标
        - center_y: 圆心 Y 坐标
        - radius: 圆的半径
        - text_content: 鼠标悬停时显示的文本内容
        """
        super().__init__(parent)
        
        # 初始化 Canvas
        self.canvas = canvas;
        self.width = 900
        self.height = 500
        
        # 圆心和半径
        self.center_x = center_x
        self.center_y = center_y
        self.radius = radius
        self.text_content = text_content
        # 使用Pillow加载WebP图片
        self.circle_id = None
        self.image_id = None
        # 使用Pillow加载WebP图片
        self.image_origin = Image.open(image_path)  # 使用Pillow打开WebP图片
        self.id = id
        self.level = 0;
 
    def draw(self):
        # 计算矩形坐标
        x0 = self.center_x - self.radius
        y0 = self.center_y - self.radius
        x1 = self.center_x + self.radius
        y1 = self.center_y + self.radius
        
        # 绘制圆圈
        self.circle_id = self.canvas.create_oval(x0, y0, x1, y1, outline="white", width=2, fill="black", tags=self.name+'_circle_' + str(self.id))
        print(self.name, str(self.id), "create_circle", self.circle_id, self.name+'_circle_'+ str(self.id), self.center_x, self.center_y)

        # 计算目标图片大小（例如，圆的直径）
        target_size = int(2 * self.radius)
        
        # 缩放图片到目标大小
        image = self.image_origin.resize((target_size, target_size), Image.Resampling.LANCZOS)
        if self.dbright is not None:
            enhancer = ImageEnhance.Brightness(image)
            image = enhancer.enhance(self.image_bright * (self.level + 1))
            print(self.name, str(self.id), "enhance", self.image_bright * self.level)
        self.image = ImageTk.PhotoImage(image)  # 转换为tkinter可用的格式
        # 在圆圈中心添加图片
        if(self.image_id is None):
            self.image_id = self.canvas.create_image(self.center_x, self.center_y, image=self.image, tags=self.name+'image'+str(self.id))
            # 绑定鼠标事件
            self.canvas.tag_bind(self.image_id, "<Enter>", self.show_popup)  # 鼠标进入图片
            self.canvas.tag_bind(self.image_id, "<Leave>", self.hide_popup)  # 鼠标离开图片
            print(self.name, str(self.id), "create_image", self.image_id, self.name+'image'+str(self.id), self.center_x, self.center_y)
        else:
            self.canvas.coords(self.image_id, [self.center_x, self.center_y])
            self.canvas.itemconfig(self.image_id, image=self.image)

        
        # 隐藏的文本对象，初始时不显示
        self.popup = None

    def on_resize(self, scale_x, scale_y, scale):
        print(self, "on_resize")
        self.current_scale = max(scale_x, scale_y)
        self.center_x = self.center_x * scale_x
        self.center_y = self.center_y * scale_y
        self.radius = self.radius * scale

        if self.circle_id is not None:
            self.canvas.delete(self.circle_id)
            self.circle_id = None
        if self.image_id is not None:
            self.canvas.delete(self.image_id)
            self.image_id = None
        self.draw()

    def show_popup(self, event):
        """鼠标悬停时显示弹窗"""
        if self.popup is None:  # 确保只有一个弹窗
            self.popup = tk.Toplevel(self)
            self.popup.wm_overrideredirect(True)  # 移除弹窗标题栏
            self.popup.geometry(f"150x50+{event.x_root+10}+{event.y_root+10}")  # 弹窗位置
            # 弹窗内容
            label = tk.Label(self.popup, text='\n'.join(self.text_content),
                             padx=10, pady=5 , wraplength=150)
            label.pack()

    def hide_popup(self, event):
        """鼠标离开时关闭弹窗"""
        if self.popup is not None:
            self.popup.destroy()  # 销毁弹窗
            self.popup = None


class SmallTalentCircle(TalentCircle):
    def __init__(self, parent, canvas, center_x, center_y, radius, text_content, level_up_time, image_path, id):
        self.level_up_time = level_up_time
        self.dbright = 1 / (level_up_time + 2)
        self.image_bright = self.dbright
        self.name = 'small_talent'
        super().__init__(parent, canvas, center_x, center_y, radius, text_content, image_path, id)
    
    def update_image(self):
        print(self.id, "update_image", self.level)
        """
        根据亮度调整图像
        :param brightness: 新的亮度值 (1.0为默认亮度)
        """
        # 计算目标图片大小（例如，圆的直径）
        target_size = int(2 * self.radius)
        image = self.image_origin.resize((target_size, target_size), Image.Resampling.LANCZOS)
        enhancer = ImageEnhance.Brightness(image)
        self.image_bright = (self.level + 1) * self.dbright + 0.3
        bright_image = enhancer.enhance(self.image_bright)
        # 转换为 Tkinter 格式的图像
        self.image = ImageTk.PhotoImage(bright_image)
        # 更新 Canvas 上的图像，不改变 image_id
        self.canvas.itemconfig(self.image_id, image=self.image)



class CoreTalentCircle(TalentCircle):
    def __init__(self, parent, canvas, center_x, center_y, radius, text_content, image_path, id):
        self.dbright = None
        self.name = 'core_talent'
        super().__init__(parent, canvas, center_x, center_y, radius, [text_content], image_path, id)

    def show_popup(self, event):
        """鼠标悬停时显示弹窗"""
        if self.popup is None:  # 确保只有一个弹窗
            self.popup = tk.Toplevel(self)
            self.popup.wm_overrideredirect(True)  # 移除弹窗标题栏
            self.popup.pack_propagate(True)
            self.popup.geometry(f"150x50+{event.x_root+10}+{event.y_root+10}")  # 弹窗位置
            # 弹窗内容
            label = tk.Label(self.popup, text=self.text_content[0], 
                             padx=10, pady=15 , wraplength=100)
            label.pack()


@dataclass
class TalentInfo:
    id: str
    talent_circle: SmallTalentCircle
    level: int 
    level_up_time: int
    talent_data: dict
    dependent_on = None  # 被依赖的天赋

    def is_up_to_limit(self):
        return self.level >= int(self.talent_data['level_up_time'])

class SmallTalent():
    def __init__(self, talent_name, data, talent_pad, img_save_dir, width, height):
        self.talent_name = talent_name
        self.data = data
        self.talent_pad = talent_pad
        self.img_save_dir = img_save_dir
        self.talent_circles = []
        self.talent_info_map = {}
        self.depence_lines  = []
        self.at_least_points  = []
        self.width = width
        self.height = height
        
        self.at_least_set = set()
        self.at_least_map = {}
        for i in self.data:
            self.at_least_set.add(self.data[i]['at_least'])
            self.at_least_map[self.data[i]['at_least']] = []

        self.dx = (self.width*0.9)/max(self.at_least_set)
        self.padx = self.width * 0.04
        self.pady = self.height * 0.04
        self.total_point = 0

    def draw_at_least_point(self, x, y, radius, text_content):
        x0 = x - radius
        y0 = y - radius
        x1 = x + radius
        y1 = y + radius
        # 绘制圆圈
        self.circle = self.talent_pad.create_oval(x0, y0, x1, y1, outline="white", width=2, fill="white", tags = "at_least_point_circle")

        # 在圆形中心显示数字
        self.text = self.talent_pad.create_text(
            x, y, text=text_content, fill="black", font=("Arial", int(radius), "bold"), tags = "at_least_point_text"
        )
        return (self.circle, self.text, x, y, radius, text_content)

    def draw(self):
        self.talent_pad.delete("all")
        for i in self.at_least_set:
            self.at_least_points.append(
                self.draw_at_least_point(i*self.dx + self.padx, 15, 15, i))

        dependent_ons = []
        for i in self.data :
            talent = self.data[i]
            talent_circle = SmallTalentCircle(self.talent_pad, self.talent_pad, 
                                            center_x=int(talent['at_least'])*self.dx + self.padx, center_y=int(talent['y']) + self.pady, 
                                            radius=32, text_content=talent['descriptions'], level_up_time = talent['level_up_time'],
                                            image_path = os.path.join(self.img_save_dir, talent['img_url'].split('/')[-1]),
                                            id = talent['id'])
            self.at_least_map[talent['at_least']].append(talent['id'])
            self.talent_circles.append(talent_circle)
            self.talent_info_map[talent['id']] = TalentInfo(talent['id'], talent_circle, level= 0, 
                                                            level_up_time = talent['level_up_time'], talent_data = talent)
            if(talent['dependce'] != None):
                depence = self.data[str(talent['dependce'])]
                dependent_ons.append((depence['id'], talent['id']))
                line = DependeceLine(self.talent_pad, x1=int(depence['at_least'])*self.dx + self.padx, y1=int(depence['y']) + self.pady, 
                                     x2=int(talent['at_least'])*self.dx + self.padx, y2=int(talent['y']) + self.pady, line_width=2,
                                     id = str(depence['id']) + "_" + str(talent['id']))
                self.depence_lines.append(line)
        
        for depence_on, talent_id in dependent_ons:
            self.talent_info_map[depence_on].dependent_on = talent_id

        return self.talent_circles, self.depence_lines

    def on_resize(self, event):
        print(self, "on_resize")
        scale_x = event.width / self.width
        scale_y = event.height / self.height
        scale = event.width * event.height / (self.width * self.height)
        self.width = event.width
        self.height = event.height
        for circle in self.talent_circles:
            circle.on_resize(scale_x, scale_y, scale)
        for line in self.depence_lines:
            line.on_resize(scale_x, scale_y, scale)
        
        at_least_points = []
        for circle, text, x, y, radius, text_content in self.at_least_points:
            self.talent_pad.delete(circle)
            self.talent_pad.delete(text)
            at_least_points.append(self.draw_at_least_point(int(x * scale_x), int(y * scale_y), int(radius * scale), text_content))
        self.at_least_points = at_least_points

    def update_bright_by_total(self, total_point):
        print("update_bright_by_total", total_point)
        if total_point in self.at_least_map:
            for i in self.at_least_map[total_point]:
                if(self.talent_info_map[i].level == 0):
                    self.talent_info_map[i].talent_circle.update_image()

    def level_up(self, talent_id, total_point):
        depence_talent_info = None
        talent_info = self.talent_info_map[talent_id]
        assert talent_info.level == talent_info.talent_circle.level;
        print(self.talent_name, "enter level_up", talent_id, talent_info.level, talent_info.talent_data['descriptions'])
        if(talent_info.talent_data['dependce'] != None):
            depence_talent_info = self.talent_info_map[talent_info.talent_data['dependce']]
        
        if(talent_info.talent_data['at_least'] > total_point):
            print("at_least", total_point, "<", talent_info.talent_data['at_least'])
            return False

        if(depence_talent_info == None and (not talent_info.is_up_to_limit())):
            talent_info.level += 1
            talent_info.talent_circle.level += 1;
            print("level_up", talent_id, talent_info.level)
            talent_info.talent_circle.update_image()
            return True
        
        if(depence_talent_info != None and depence_talent_info.is_up_to_limit() and (not talent_info.is_up_to_limit())):
            talent_info.level += 1
            talent_info.talent_circle.level += 1;
            print("level_up", talent_id, talent_info.level)
            talent_info.talent_circle.update_image()
            return True
        return False
    
    def SetTalentInfo(self, new_talent_info_map):
        print(self.talent_name, "SetTalentInfo")
        for i in new_talent_info_map:
            talent_info = new_talent_info_map[i]
            self.talent_info_map[i].level = talent_info.level
            self.talent_info_map[i].talent_circle.level = talent_info.level
            if(talent_info.level > 0):
                print(self.talent_name, i, talent_info.level)
                print("update_image", i, talent_info.level)
                self.talent_info_map[i].talent_circle.update_image()
    
    def level_down(self, talent_id, total_point):
        talent_info = self.talent_info_map[talent_id]
        dependent_on = talent_info.dependent_on
        assert talent_info.level == talent_info.talent_circle.level;
        print(self.talent_name, "enter level_down", talent_id, talent_info.level, talent_info.talent_data['descriptions'])
        if(dependent_on != None):
            dependent_on_info = self.talent_info_map[dependent_on]
            if(dependent_on_info.level > 0):
                return False

        if(talent_info.level > 0):
            if total_point in self.at_least_map:
                for i in self.at_least_map[total_point]:
                    if(self.talent_info_map[i].level > 0):
                        return False
            talent_info.level -= 1
            talent_info.talent_circle.level -= 1;
            talent_info.talent_circle.update_image()
            return True

class CoreTalent():
    def __init__(self, talent_name, data, core_talent_pad, img_save_dir, width, height):
        self.talent_name = talent_name
        self.data = data
        self.core_talent_pad = core_talent_pad
        self.img_save_dir = img_save_dir
        self.first_core = []
        self.second_core = []
        self.width = width
        self.height = height
        self.dx = self.width / 9
        self.core_talent_pad.delete("all")
    
    def draw(self):
        for talent in [self.data['0'], self.data['1'], self.data['2']]:
            talent_circle = CoreTalentCircle(self.core_talent_pad, self.core_talent_pad, 
                                             center_x=talent["id"]*self.dx+self.dx, center_y=self.height/2, 
                                radius=10, text_content=talent['descriptions'], 
                                image_path = os.path.join(self.img_save_dir, talent['img_url'].split('/')[-1]),
                                id = talent['id'])
            self.first_core.append(talent_circle)

        for talent in [self.data['3'], self.data['4'], self.data['5']]:
            talent_circle = CoreTalentCircle(self.core_talent_pad, self.core_talent_pad, 
                                             center_x=talent["id"]*self.dx + self.dx * 3, center_y=self.height/2, 
                                radius=10, text_content=talent['descriptions'], 
                                image_path = os.path.join(self.img_save_dir, talent['img_url'].split('/')[-1]),
                                id = talent['id'])
            self.second_core.append(talent_circle)
            

    def on_resize(self, event):
        print(self, "on_resize")
        scale_x = event.width / self.width
        scale_y = 1
        scale = event.width * event.height / (self.width * self.height)
        self.width = event.width
        self.height = event.height
        for circle in self.first_core:
            circle.on_resize(scale_x, scale_y, scale)
        for circle in self.second_core:
            circle.on_resize(scale_x, scale_y, scale)

class DrawSwitchTalent():
    def __init__(self):
        print("DrawSwitchTalent")

class TalentData():
    talent_data = None
    talent_group_map = None
    def __init__(self):
        self.list_path = "./out/talent_list.txt"
        if TalentData.talent_data is None or TalentData.talent_group_map is None:
            TalentData.talent_data, TalentData.talent_group_map = self.ReadTalentData()
    def ReadTalentData(self):
        if TalentData.talent_data is not None and TalentData.talent_group_map is not None:
            return TalentData.talent_data, TalentData.talent_group_map
        try:
            talent_name_map = read_json_to_map(self.list_path)
            
            talent_list = [item for group in talent_name_map for item in group]
            base_talent_list = [item for item in talent_list[0::4]]
            
            talent_group_map = {}
            for i, base_talent in enumerate(base_talent_list):
                talent_group_map[base_talent[0]] = talent_list[i*4+1:i*4+4]
            
            talent_data = {}
            
            for talent in talent_list:
                path_bath = f"./out/talent_{talent[1]}.txt"
                talent_data[talent[0]] = {'data': read_json_to_map(path_bath), 'url':talent[2]}
            return talent_data, talent_group_map

        except Exception as e:
            print(f"读取 JSON 文件失败: {e}")


def drawImage(canvas, image_path, radius, center_x, center_y):
    # 使用Pillow加载WebP图片
    image = Image.open(image_path)  # 使用Pillow打开WebP图片

    # 计算目标图片大小（例如，圆的直径）
    target_size = int(2 * radius)
    
    # 缩放图片到目标大小
    image = image.resize((target_size, target_size), Image.Resampling.LANCZOS)

    image = ImageTk.PhotoImage(image)  # 转换为tkinter可用的格式
    
    # 在圆圈中心添加图片
    image_id = canvas.create_image(center_x, center_y, image = image)
    return image_id
